package com.retail.xiaocui.server.look;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;


public class RedisLuaLook implements Lock {



    // 3个参数  锁名称 ， redisTemplate ， uuid
    private StringRedisTemplate redisTemplate;
    private String lockName;

    private String uuid;

    //写构造方法
    public RedisLuaLook(StringRedisTemplate redisTemplate, String lockName,String uuid) {
        this.redisTemplate = redisTemplate;
        this.lockName = lockName;
        this.uuid= uuid+":"+Thread.currentThread().getId();
    }

    //参数  过期时间
    private long expire =30;

    @Override
    public void lock() {

        //调用 无参 tryLock方法
        this.tryLock();

    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        try {
            //调用 有参 tryLock方法
            return this.tryLock(-1L,TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {

        //判断是否定义过期时间 ，如果未定义 则用默认30秒的过期时间
        if (time != -1){

            this.expire=unit.toSeconds(time);
        }

        //lua脚本代码
        String script = "if redis.call('exists', KEYS[1]) == 0 or redis.call('hexists',KEYS[1],ARGV[1]) == 1 " +
                "then "+
                "   redis.call('hincrby',KEYS[1],ARGV[1], 1) " +
                "   redis.call('expire',KEYS[1],ARGV[2]) " +
                "   return 1 " +
                "else " +
                "   return 0 " +
                "end";
               //用redis 的executu方法  使用lua 脚本
        while ( !this.redisTemplate.execute(new DefaultRedisScript<>(script,Boolean.class), Arrays.asList(lockName),uuid,String.valueOf(expire))){

            Thread.sleep(50);
        }

        //加锁成功 ，返回之前 ，开启定时器自动
        this.renewExpire();

        return true;
    }



    //解锁方法
    @Override
    public void unlock() {

        //解锁 lua脚本代码
        String script= "if redis.call('hexists', KEYS[1], ARGV[1]) == 0 "+
                "then "+
                "   return nil  "+
                "elseif redis.call('hincrby', KEYS[1], ARGV[1], -1) == 0 "+
                "then "+
                "   return redis.call('del', KEYS[1]) "+
                "else "+
                "   return 0 " +
                "end";


        //调用redisTemplate.execute 方法 使用lua 脚本 顺便 传入参数
        Long flag = this.redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lockName), uuid);

        System.out.println(flag);

        //判断 是否是自己的锁 如果不是则抛出异常
        if (flag == null){
            throw new IllegalMonitorStateException("这个锁不属于你");
        }
    }

    @Override
    public Condition newCondition() {
        return null;
    }




    //锁 自动续期 方法
    private void renewExpire(){

        //自动续期lua 代码
        String script="if redis.call('hexists', KEYS[1], ARGV[1]) == 1 " +
                "then  " +
                "   return redis.call('expire', KEYS[1], ARGV[2]) " +
                "else " +
                "   return 0 " +
                "end ";

        //  JAVA.util提供的Timer定时器 Timer.schedule 调用自动续期lua脚本代码 实现自动续期
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {

               if (redisTemplate.execute(new DefaultRedisScript<>(script,Boolean.class),Arrays.asList(lockName),uuid,String.valueOf(expire))){

                   renewExpire();
                };

            }
        },this.expire * 1000 / 3);
    }

}
